using System;
using System.Collections.Generic;
using System.Text;

namespace Decav.Gatsb.Application.CommandInterpreters
{
    public class SetLocationCommandInterpreter : ICommandInterpreter
    {
        #region ICommandInterpreter Members

        public void Execute(CommandInterpreterContext context)
        {
            if (!context.MessageHasText || context.ResponseEnded)
                return;

            string locationName = null;
            if (!context.FindCommand(_commandPrefixes, out locationName))
                return;

            FindLocation(context, locationName);
        }

        private static readonly string[] _commandPrefixes = new string[] {
            "@", "at ", "i'm at ", "im at ", "i am at ",
            "in ", "im in ", "i'm in ", "i am in " }; 

        #endregion

        private void FindLocation(CommandInterpreterContext context, string locationName)
        {
            Location[] locations = GatsbApplication.Current.FindLocation(new LocationSearchCriteria(locationName, GatsbApplication.Current.CurrentUser.PostalCode));

            if (locations.Length == 0)
            {
                context.SendResponse(string.Format("No location found with the name '{0}'.", locationName));

                context.EndParse();
                context.ShouldPost = false;
            }
            else if (locations.Length == 1)
            {
                SetCurrentLocation( locations[0], context );
            }
            else
            {
                Location exactMatch = FindAbsoluteLocation(locationName, locations);
                if (exactMatch != null)
                {
                    SetCurrentLocation(exactMatch, context);
                    return;
                }

                // We found lots of locations, so we need to choose one.... Send a message back and ask which one.
                StringBuilder builder = new StringBuilder();
                builder.AppendFormat("Results for {0}:\r\n", locationName);

                CreateLocationList(builder, locations);

                // If we have enough room left, throw in some instructions
                string endString = "\r\nReply with # of your choice.";
                if (builder.Length + endString.Length <= MaxTextMessageLength)
                    builder.Append(endString);

                context.SendResponse(builder.ToString());

                //TODO: Make a session for the communications.

                context.RemoveFirstBodyLine();
            }
        }

        public const int MaxTextMessageLength = 160;

        private void SetCurrentLocation(Location loc, CommandInterpreterContext context)
        {
            // Set the location
            GatsbApplication.Current.SetUserLocation(loc.Id);
            context.SendResponse(string.Format(
                "Okay, you're at {0} ({1}).\r\n\r\nSend a txt of picture to post here.",
                loc.Name, loc.Address.Address1));

            context.RemoveFirstBodyLine();
        }

        private Location FindAbsoluteLocation(string locationName, Location[] locations)
        {
            Location exactMatch = null;
            bool multiExactMatches = false;
            foreach (Location loc in locations)
            {
                // If the location is an exact match, and there is only one exact match,
                // this is the one to return.
                if (string.Compare(loc.Name, locationName, true) == 0)
                {
                    if (exactMatch == null)
                        exactMatch = loc;
                    else
                        multiExactMatches = true;
                }
            }

            if (!multiExactMatches && exactMatch != null)
                return exactMatch;

            return null;
        }

        /// <summary>
        /// Creates a list of locations from an array.
        /// </summary>
        /// <param name="builder">The string builder to append to.</param>
        /// <param name="locations">The array of locations to write.</param>
        public static void CreateLocationList(StringBuilder builder, Location[] locations)
        {
            if (builder == null)
                throw new ArgumentNullException("builder");

            if (locations == null)
                throw new ArgumentNullException("locations");

            for (int i = 0; i < locations.Length; i++)
            {
                string name = locations[i].Name;
                string entry = string.Format("{0}) {1}\r\n", i + 1, name);

                if (builder.Length + entry.Length > MaxTextMessageLength)
                    break;

                builder.Append(entry);
            }
        }
    }
}
